// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


`include "espi_header.iv"

`timescale 1 ps / 1 ps
module espi_cmd_det #(
    parameter DATABYTE_ARR = 2,
    parameter HDRBYTE_ARR = 4
)(
    input                             clk,
    input                             reset_n,
    input                             stop_det,
    input                             rx_detect_command_end,
    input                             rx_detect_cycletype_end,
    input                             rx_detect_header,
    input [7:0]                       header_byte[HDRBYTE_ARR],
    input [7:0]                       data_byte[DATABYTE_ARR],
    input [7:0]                       command_byte,
    input [6:0]                       length_byte,
    input [4:0]                       cycletype_byte,
    input [2:0]                       cmd_cycletype_msb,
    input [7:0]                       crc_byte,
    output logic [15:0]               config_reg_addr,
    output logic [31:0]               config_reg_datain,
    output logic [3:0]                cmd_hdr_ptr,
    output logic [6:0]                cmd_data_ptr,
    output logic [2:0]                cmd_data_array,
    output logic                      detect_getvwire,
    output logic                      detect_getstatus,
    output logic                      detect_getconfig,
    output logic                      detect_setconfig,
    output logic                      detect_putpc,
    output logic                      detect_putnp,
    output logic                      detect_getpc,
    output logic                      detect_getnp,
    output logic                      detect_putvwire,
    output logic                      detect_put_np_rxfifo,
    output logic                      detect_put_pc_rxfifo,
    output logic                      detect_get_np_txfifo,
    output logic                      detect_get_pc_txfifo,
    output logic                      detect_pc_cmd_withdata,
    output logic                      detect_iord_short,
    output logic                      detect_iowr_short,
    output logic                      detect_short_1b,
    output logic                      detect_short_2b,
    output logic                      detect_short_4b,
    output logic                      detect_memrd_short,
    output logic                      detect_memwr_short,
    output logic                      detect_reset,
    output logic                      detect_putoob,
    output logic                      detect_getoob,
    output logic                      detect_put_oob_rxfifo,
    output logic                      detect_get_oob_txfifo,
    output logic                      posted_cycle,
    output logic                      invalid_cmd,
    output logic                      invalid_cycletype,
    output logic                      rx_with_cycletype,
    output logic [7:0]                vw_data_ptr,
    output logic [3:0]                addr_ptr

);

logic detect_ch0_cmd;
logic detect_ch1_cmd;
logic detect_ch2_cmd;
logic detect_config_cmd;
logic detect_ioshort_cmd;
logic detect_memshort_cmd;
logic detect_iord_short1b;
logic detect_iord_short2b;
logic detect_iord_short4b;
logic detect_iowr_short1b;
logic detect_iowr_short2b;
logic detect_iowr_short4b;
logic detect_memrd32_short1b;
logic detect_memrd32_short2b;
logic detect_memrd32_short4b;
logic detect_memwr32_short1b;
logic detect_memwr32_short2b;
logic detect_memwr32_short4b;
logic [3:0] length_ptr;
logic [3:0] cmd_hdr_ptr_combi;
logic [6:0] cmd_data_ptr_combi;
logic [7:0] vw_data_ptr_int;

// Channel Non-specific
assign detect_reset          = (command_byte == `RESET);
assign detect_getconfig      = (command_byte == `GET_CONFIGURATION);
assign detect_setconfig      = (command_byte == `SET_CONFIGURATION);
assign detect_getstatus      = (command_byte == `GET_STATUS);

// Peripheral Channel (0)
assign detect_putnp          = (command_byte == `PUT_NP);
assign detect_putpc          = (command_byte == `PUT_PC);
assign detect_getpc          = (command_byte == `GET_PC);
assign detect_getnp          = (command_byte == `GET_NP);
assign detect_iord_short1b   = (command_byte == `PUT_IORD_SHORT_1B);
assign detect_iord_short2b   = (command_byte == `PUT_IORD_SHORT_2B);
assign detect_iord_short4b   = (command_byte == `PUT_IORD_SHORT_4B);
assign detect_iowr_short1b   = (command_byte == `PUT_IOWR_SHORT_1B);
assign detect_iowr_short2b   = (command_byte == `PUT_IOWR_SHORT_2B);
assign detect_iowr_short4b   = (command_byte == `PUT_IOWR_SHORT_4B);
assign detect_memrd32_short1b = (command_byte == `PUT_MEMRD32_SHORT_1B);
assign detect_memrd32_short2b = (command_byte == `PUT_MEMRD32_SHORT_2B);
assign detect_memrd32_short4b = (command_byte == `PUT_MEMRD32_SHORT_4B);
assign detect_memwr32_short1b = (command_byte == `PUT_MEMWR32_SHORT_1B);
assign detect_memwr32_short2b = (command_byte == `PUT_MEMWR32_SHORT_2B);
assign detect_memwr32_short4b = (command_byte == `PUT_MEMWR32_SHORT_4B);

// Virtual Wire Channel (1)
assign detect_getvwire       = (command_byte == `GET_VWIRE);
assign detect_putvwire       = (command_byte == `PUT_VWIRE);

// Out-of-Band Channel (2)
assign detect_putoob         = (command_byte == `PUT_OOB);
assign detect_getoob         = (command_byte == `GET_OOB);

// Command classifications
assign detect_iord_short     = detect_iord_short1b | detect_iord_short2b | detect_iord_short4b;
assign detect_iowr_short     = detect_iowr_short1b | detect_iowr_short2b | detect_iowr_short4b;
assign detect_memrd_short    = detect_memrd32_short1b | detect_memrd32_short2b | detect_memrd32_short4b;
assign detect_memwr_short    = detect_memwr32_short1b | detect_memwr32_short2b | detect_memwr32_short4b;
assign detect_short_1b       = detect_iord_short1b | detect_iowr_short1b |
                               detect_memrd32_short1b | detect_memwr32_short1b;
assign detect_short_2b       = detect_iord_short2b | detect_iowr_short2b |
                               detect_memrd32_short2b | detect_memwr32_short2b;
assign detect_short_4b       = detect_iord_short4b | detect_iowr_short4b |
                               detect_memrd32_short4b | detect_memwr32_short4b;
    

assign detect_pc_cmd_withdata = ((detect_putpc) & (cycletype_byte == `MEM_WRITE_32 |
                                                   cycletype_byte == `MEM_WRITE_64 |
                                                   cycletype_byte == `LOCAL_MESSAGE_DATA |
                                                   cycletype_byte == `SUCCESSFUL_COMPLETE_DATA));
assign detect_ioshort_cmd   = (detect_iord_short | detect_iowr_short);
assign detect_memshort_cmd  = (detect_memrd_short | detect_memwr_short);
assign detect_put_np_rxfifo = detect_putnp;
assign detect_put_pc_rxfifo = detect_putpc;
assign detect_config_cmd    = (detect_getconfig || detect_setconfig);
assign detect_get_np_txfifo = detect_getnp;
assign detect_get_pc_txfifo = detect_getpc;
assign detect_put_oob_rxfifo = detect_putoob;
assign detect_get_oob_txfifo = detect_getoob;

assign posted_cycle = detect_getconfig | detect_setconfig | detect_getvwire | detect_putvwire | 
                      detect_getstatus | detect_putpc | detect_getpc | detect_putoob | detect_getoob;

assign detect_ch0_cmd = (detect_putnp | detect_getnp |
                         detect_putpc | detect_getpc |
                         detect_memrd_short | detect_memwr_short |
                         detect_iord_short | detect_iowr_short);
assign detect_ch1_cmd = (detect_getvwire | detect_putvwire);
assign detect_ch2_cmd = (detect_getoob | detect_putoob);

assign cmd_hdr_ptr_combi = (detect_putpc || detect_putnp) ? addr_ptr + length_ptr + 4'd1 :
                           (detect_putoob) ? 4'd3 :
                           (detect_ioshort_cmd) ? 4'd2 :
                           (detect_memshort_cmd) ? 4'd4 :
                           (detect_putvwire) ? 4'd1 :
                           (detect_config_cmd) ? 4'd2 :
                           4'd0;

assign rx_with_cycletype = (rx_detect_command_end & (~|command_byte[7:2]) & ~command_byte[0]);
assign cmd_data_array = (detect_putvwire) ? 3'd2 : 3'd4;

assign vw_data_ptr_int = vw_data_ptr + 8'd1;
assign cmd_data_ptr_combi   = (detect_pc_cmd_withdata) ? length_byte :
                              (detect_putoob) ? header_byte[2][6:0] :
                              (detect_iowr_short1b || detect_memwr32_short1b) ? 7'd1 :
                              (detect_iowr_short2b || detect_memwr32_short2b) ? 7'd2 :
                              (detect_iowr_short4b || detect_memwr32_short4b) ? 7'd4 :
                              (detect_putvwire) ? vw_data_ptr_int[6:0] << 7'd1 :
                              (detect_setconfig) ? 7'd4 :
                              7'd0;
assign invalid_cmd = (rx_detect_command_end &&
                      (~(detect_ch0_cmd | detect_ch1_cmd | detect_ch2_cmd |
                         detect_config_cmd | detect_getstatus | detect_reset)));
assign invalid_cycletype = (rx_detect_cycletype_end &&
                            (((addr_ptr == 4'b1111) && (length_ptr == 4'b1111)) ||
                             (|cmd_cycletype_msb)));

initial config_reg_addr <= 0;
always @(posedge clk) begin
    if (detect_getconfig || detect_setconfig) begin
        config_reg_addr <= {header_byte[0], header_byte[1]};
    end
end

initial config_reg_datain <= 32'h0;
always @(posedge clk) begin
    if (detect_setconfig) begin
        config_reg_datain <= {data_byte[3], data_byte[2], data_byte[1], data_byte[0]};
    end
end

always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        cmd_data_ptr         <= 7'd0;
        cmd_hdr_ptr          <= 4'd0;
    end
    else begin
        cmd_data_ptr         <= cmd_data_ptr_combi;
        cmd_hdr_ptr          <= cmd_hdr_ptr_combi;
    end
end

always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        addr_ptr      <= 4'd0;
        length_ptr    <= 4'd0;
    end
    else if (rx_detect_header) begin
        case ({command_byte, cycletype_byte})
        {`PUT_PC, `MEM_WRITE_32} : begin
                addr_ptr      <= 4'd4;          // 32-bit address
                length_ptr    <= 4'd2;          // 2-byte length
        end
        {`PUT_PC, `MEM_WRITE_64} : begin
                addr_ptr      <= 4'd8;          // 64-bit address
                length_ptr    <= 4'd2;          // 2-byte length
        end
        {`PUT_PC, `LOCAL_MESSAGE} : begin
                addr_ptr      <= 4'd5;          // 5 Message specific Byte
                length_ptr    <= 4'd2;          // 2 byte length
        end
        {`PUT_PC, `LOCAL_MESSAGE_DATA} : begin
                addr_ptr      <= 4'd5;          // 5 Message specific Byte
                length_ptr    <= 4'd2;          // 2 byte length
        end
        {`PUT_PC, `UNSUCCESSFUL_COMPLETE} : begin
                addr_ptr      <= 4'd0;
                length_ptr    <= 4'd2;          // 2-byte length
        end
        {`PUT_PC, `SUCCESSFUL_COMPLETE_DATA} : begin
                addr_ptr      <= 4'd0;
                length_ptr    <= 4'd2;          // 2-byte length
        end
        {`PUT_NP, `MEM_READ_32} : begin
                addr_ptr      <= 4'd4;          // 32-bit address
                length_ptr    <= 4'd2;          // 2-byte length
        end
        {`PUT_NP, `MEM_READ_64} : begin
                addr_ptr      <= 4'd8;          // 64-bit address
                length_ptr    <= 4'd2;          // 2-byte length
        end
        default: begin
                addr_ptr      <= 4'b1111;
                length_ptr    <= 4'b1111;
        end
        endcase
    end
end

always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        vw_data_ptr         <= 8'd0;
    end
    else if (stop_det) begin
        vw_data_ptr         <= 8'd0;
    end
    else if (detect_putvwire) begin
        vw_data_ptr         <= header_byte[0];
    end
end

endmodule
